﻿
// Пример диагностики, которая не только находит ошибку, но и предлагает замену (автоматическое исправление).
// Плагин обнаруживает использование конструкторов структур, регистрирует ошибку и замену в виде вставки ключей.
// Пример:
//   Сотрудник = Новый Структура("Имя, Фамилия", "Иван");
// будет заменено на:
//   Сотрудник = Новый Структура;
//   Сотрудник.Вставить("Имя", "Иван");
//   Сотрудник.Вставить("Фамилия", Неопределено);

Перем Токены;
Перем Типы;
Перем ТаблицаТокенов;
Перем Исходник;
Перем ТаблицаОшибок;
Перем ТаблицаЗамен;
Перем Стек;

Перем ФрагментыПоИменам;

Процедура Открыть(Парсер, Параметры) Экспорт
	
	Токены = Парсер.Токены();
	ТаблицаТокенов = Парсер.ТаблицаТокенов();
	Исходник = Парсер.Исходник();
	Типы = Парсер.Типы();
	ТаблицаОшибок = Парсер.ТаблицаОшибок();
	ТаблицаЗамен = Парсер.ТаблицаЗамен();
	Стек = Парсер.Стек();
	
	ФрагментыПоИменам = Новый Структура;
	
	ФрагментыНаРусском = Новый Структура;
	ФрагментыНаРусском.Вставить("Фрагмент1", "Новый Структура;");
	ФрагментыНаРусском.Вставить("Фрагмент2", ".Вставить(""%1"", ");
	ФрагментыНаРусском.Вставить("Фрагмент3", "Неопределено");
	
	ФрагментыПоИменам.Вставить("Структура", ФрагментыНаРусском);
	
	ФрагментыНаАнглийском = Новый Структура;
	ФрагментыНаАнглийском.Вставить("Фрагмент1", "New Structure;");
	ФрагментыНаАнглийском.Вставить("Фрагмент2", ".Insert(""%1"", ");
	ФрагментыНаАнглийском.Вставить("Фрагмент3", "Undefined");
		
	ФрагментыПоИменам.Вставить("Structure", ФрагментыНаАнглийском);
	
КонецПроцедуры // Открыть()

Функция Закрыть() Экспорт
	
	Возврат Неопределено;	
	
КонецФункции // Закрыть()

Функция Подписки() Экспорт
	Перем Подписки;
	Подписки = Новый Массив;
	Подписки.Добавить("ПосетитьВыражениеНовый");	
	Возврат Подписки;
КонецФункции // Подписки()

#Область РеализацияПодписок

Процедура ПосетитьВыражениеНовый(ВыражениеНовый) Экспорт
	
	Фрагменты = Неопределено;
	
	Если ВыражениеНовый.Аргументы = Неопределено
		Или ВыражениеНовый.Аргументы.Количество() = 0
		Или Не ФрагментыПоИменам.Свойство(ВыражениеНовый.Имя, Фрагменты) Тогда
		Возврат; // Либо аргументов конструктора нет, либо это не структура	
	КонецЕсли; 
	
	ТекстОшибки = "Не рекомендуется использовать конструктор структур."; 
	
	ПервыйАргумент = ВыражениеНовый.Аргументы[0];
		
	Если ПервыйАргумент.Тип <> Типы.ВыражениеСтроковое Тогда
		Ошибка(ТекстОшибки, ВыражениеНовый.Начало, ВыражениеНовый.Конец);
		Возврат; // Замены выполняются только для ключей заданных строкой
	КонецЕсли; 
		
	Родитель = Стек[Стек.ВГраница()];
	
	Если Родитель.Тип <> Типы.ОператорПрисваивания Тогда
		Ошибка(ТекстОшибки, ВыражениеНовый.Начало, ВыражениеНовый.Конец);
		Возврат; // Замены выполняются только для присваиваний
	КонецЕсли; 
		
	Идентификатор = Родитель.ЛевыйОперанд;
	
	Если Идентификатор.Аргументы <> Неопределено
		Или Идентификатор.Хвост.Количество() <> 0 Тогда
		Ошибка(ТекстОшибки, ВыражениеНовый.Начало, ВыражениеНовый.Конец);
		Возврат; // Замены выполняются только для простых переменных
	КонецЕсли; 
					
	ПервыйТокенИдентификатора = Идентификатор.Начало;
	ДлинаОтступа = ПервыйТокенИдентификатора.НомерКолонки - 1;
	Отступ = Сред(Исходник, ПервыйТокенИдентификатора.Позиция - ДлинаОтступа, ДлинаОтступа);
	
	Если Не ПустаяСтрока(Отступ) Тогда
		Ошибка(ТекстОшибки, ВыражениеНовый.Начало, ВыражениеНовый.Конец);
		Возврат; // Что-то есть перед переменной в строке. Замена невозможна	
	КонецЕсли; 
		
	СписокКлючей = Новый Массив;
	
	Для Каждого Элемент Из ПервыйАргумент.Элементы Цикл
		СписокКлючей.Добавить(Элемент.Значение);
	КонецЦикла; 
	
	Попытка
		Ключи = СтрРазделить(СтрСоединить(СписокКлючей), ",", Ложь);
	Исключение
		Ошибка(ТекстОшибки, ВыражениеНовый.Начало, ВыражениеНовый.Конец);
		Возврат; // Что-то пошло не так. Замена невозможна
	КонецПопытки;
	
	Буфер = Новый Массив;
	
	КоличествоЗначений = ВыражениеНовый.Аргументы.Количество() - 1;
	
	Буфер.Добавить(Фрагменты.Фрагмент1);
	
	Для Индекс = 0 По Ключи.Количество() - 1 Цикл
		
		Ключ = СокрЛП(Ключи[Индекс]);
		
		Буфер.Добавить(Символы.ПС);
		Буфер.Добавить(Отступ);
		Буфер.Добавить(УчастокТекста(Исходник, Идентификатор.Начало, Идентификатор.Конец));
		Буфер.Добавить(СтрШаблон(Фрагменты.Фрагмент2, Ключ));
		
		Если Индекс < КоличествоЗначений И ВыражениеНовый.Аргументы[Индекс + 1] <> Неопределено Тогда
			Аргумент = ВыражениеНовый.Аргументы[Индекс + 1]; 
			Буфер.Добавить(УчастокТекста(Исходник, Аргумент.Начало, Аргумент.Конец));
		Иначе
			Буфер.Добавить(Фрагменты.Фрагмент3);
		КонецЕсли; 
		
		Буфер.Добавить(");");
		
	КонецЦикла; 
	
	Текст = СтрСоединить(Буфер);
	
	ПервыйТокен = ВыражениеНовый.Начало;
	ПоследнийТокен = ВыражениеНовый.Конец;
	СледующийТокен = ТаблицаТокенов[ВыражениеНовый.Конец.Индекс + 1];
	
	Если СледующийТокен.Токен = Токены.ТочкаСЗапятой Тогда
		ПоследнийТокен = СледующийТокен;
	КонецЕсли; 
	
	Ошибка(ТекстОшибки, ПервыйТокен, ПоследнийТокен, Истина);
	Замена(Текст, ПервыйТокен, ПоследнийТокен);
				
КонецПроцедуры 

#КонецОбласти

#Область ВспомогательныеМетоды

Функция УчастокТекста(Текст, Начало, Конец)
	Возврат Сред(Текст, Начало.Позиция, Конец.Позиция + Конец.Длина - Начало.Позиция); 
КонецФункции

Процедура Ошибка(Текст, Начало, Конец = Неопределено, ЕстьЗамена = Ложь)
	Ошибка = ТаблицаОшибок.Добавить();
	Ошибка.Источник = "ДетекторКонструкторовСтруктур";
	Ошибка.Текст = Текст;
	Ошибка.ПозицияНачала = Начало.Позиция;
	Ошибка.НомерСтрокиНачала = Начало.НомерСтроки;
	Ошибка.НомерКолонкиНачала = Начало.НомерКолонки;
	Если Конец = Неопределено Или Конец = Начало Тогда
		Ошибка.ПозицияКонца = Начало.Позиция + Начало.Длина;
		Ошибка.НомерСтрокиКонца = Начало.НомерСтроки;
		Ошибка.НомерКолонкиКонца = Начало.НомерКолонки + Начало.Длина;
	Иначе
		Ошибка.ПозицияКонца = Конец.Позиция + Конец.Длина;
		Ошибка.НомерСтрокиКонца = Конец.НомерСтроки;
		Ошибка.НомерКолонкиКонца = Конец.НомерКолонки + Конец.Длина;
	КонецЕсли;
	Ошибка.ЕстьЗамена = ЕстьЗамена;
КонецПроцедуры

Процедура Замена(Текст, Начало, Конец = Неопределено)
	НоваяЗамена = ТаблицаЗамен.Добавить();
	НоваяЗамена.Источник = "ДетекторКонструкторовСтруктур";
	НоваяЗамена.Текст = Текст;
	НоваяЗамена.Позиция = Начало.Позиция;
	Если Конец = Неопределено Тогда
		НоваяЗамена.Длина = Начало.Длина;
	Иначе
		НоваяЗамена.Длина = Конец.Позиция + Конец.Длина - Начало.Позиция;
	КонецЕсли;
КонецПроцедуры

#КонецОбласти
